package fr.utt.lo02.CrazyEights.model.players;


import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Observable;
import java.util.Random;

import fr.utt.lo02.CrazyEights.model.Game;
import fr.utt.lo02.CrazyEights.model.cards.Card;
import fr.utt.lo02.CrazyEights.model.cards.CardSuit;
import fr.utt.lo02.CrazyEights.model.exceptions.EmptyPileException;


/**
 * A class representing a player.
 * 
 * @author Jeremy
 * 
 */

public abstract class Player extends Observable implements Serializable{
	
	private static final long serialVersionUID = 1L;

	/**
	 * Name of the player
	 */
	private String name;
	
	/**
	 * Cards in hand of the player
	 */
	private ArrayList<Card> cardsInHand = new ArrayList<Card>();
	
	/**
	 * Create the player
	 * @param n name wanted for the player
	 */
	public Player(String n) {
		name = n;
	}
	
	public String getName(){
		return name;
	}
	
	/**
	 * This method sums the points of the Cards in cardsInHand,
	 * to get the total of points owned by the Player
	 * @return the number of points of the cards in hand
	 */
	public int getPoints() {
		int a = 0;
			for(Card d : cardsInHand)
				a += d.getRank().getPoints();
		return a;
	}
	
	/**
	 * Check if the player is without cards
	 * @return true if the player is without cards, false otherwise
	 */
	public boolean isWithoutCards(){
		return cardsInHand.isEmpty();
	}
	
	/**
	 * Show the cards in hand in a String
	 * @return a string representing the cards in hand
	 */
	public String cardsInHandToString(){
		String string = "";
		int i = 1;
		for(Card d : cardsInHand){
			string+=i+". "+d.toString()+"\n";
			i++;
		}
		return string;
	}
	
	/**
	 * Add a set of Card to the hand of the Player
	 * @param cards the set of Cards to add
	 */
	public void addCardInHand(ArrayList<Card> cards){
		for(Card d : cards)
			addCardInHand(d);
	}

	/**
	 * Add a Card to the hand of the Player
	 * @param d the Card to add
	 */
	public void addCardInHand(Card d){
		cardsInHand.add(d);
		sortHand();
		setChanged();
		notifyObservers();
	}
	
	/**
	 * Remove a card in cards in hand
	 * @param card the card to remove in hand
	 */
	public void removeCardInHand(Card card){
		cardsInHand.remove(card);
		sortHand();
		setChanged();
		notifyObservers();
	}
	
	/**
	 * Pick a random Card from the hand.
	 * The Card is automatically removed from the hand
	 * @return the Card picked
	 */
	public Card getRandomCard() {
		Random r = new Random();
		Card c = cardsInHand.get(r.nextInt(cardsInHand.size()));
		removeCardInHand(c);
		return c;
	}
	
	/**
	 * This method sort the hand of the Player
	 */
	public void sortHand() {
		ArrayList<Card> t = new ArrayList<Card>();
		// let's sort the Cards by their Rank first
		Collections.sort(cardsInHand);
		
		// then by their Suit
			for(CardSuit s : CardSuit.values())
				for(Card c: cardsInHand)
					if(c.getSuit() == s)
						t.add(c);
					
		cardsInHand = t;
	}
	
	/**
	 * the player draws one card
	 */
	public void draw() {
		this.draw(1);
	}
	
	/**
	 * the player draws the number of cards chosen
	 * @param nbOfCards The number of cards to draw
	 */
	public void draw(int nbOfCards) {
		if(!Game.getInstance().getStockPile().isEmpty()){
			try {
				cardsInHand.addAll(Game.getInstance().getStockPile().draw(nbOfCards));
			} catch (EmptyPileException e) {
				e.printStackTrace();
			}
			setChanged();
			notifyObservers();
		}
	}
	
	public ArrayList<Card> getCardsInHand(){
		return cardsInHand;
	}
	
	/**
	 * the player plays the card chosen
	 * @param card the card to play or null if the player wants to draw
	 */
	public void play(Card card){
		if(card!=null){
			if(Game.getInstance().getDiscardPile().isPlayable(card)){
				Game.getInstance().getDiscardPile().addCard(card);
				removeCardInHand(card);
				Game.getInstance().play(card);
			}
		}
		else{
			Game.getInstance().play(card);
		}
	}
	
	public int getNbCardsInHand(){
		return cardsInHand.size();
	}
	
	public abstract void play();
	
	public abstract void chooseSuit();
}
